package com.alibaba.druid.sql.dialect.kingbasesql.visitor;

import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.ast.statement.*;
import com.alibaba.druid.sql.dialect.kingbasesql.methodinvoke.imp.FindInSetMethodInvoke;
import com.alibaba.druid.sql.dialect.kingbasesql.methodinvoke.imp.IsNullSetMethodInvoke;
import com.alibaba.druid.sql.dialect.postgresql.visitor.PGOutputVisitor;

import java.util.List;

/**
 * @author jiangtao
 */
public class KingbaseOutputVisitor extends PGOutputVisitor {

    public KingbaseOutputVisitor(Appendable appender) {
        super(appender);
    }

    public KingbaseOutputVisitor(Appendable appender, boolean parameterized) {
        super(appender, parameterized);
    }

    @Override
    public void preVisit(SQLObject x) {
        preVisitCheck(this, x);
    }

    @Override
    public boolean visit(SQLIdentifierExpr x) {
        format(x);
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLSelectItem x) {
        format(x.getExpr());
        // 处理 alias
        x.setAlias(normalizeIdent(x.getAlias()));
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLPropertyExpr x) {
        format(x);
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLExprTableSource x) {
        format(x.getExpr());
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLBinaryOpExpr x) {
        format(x.getLeft());
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLUpdateSetItem x) {
        format(x.getColumn());
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLMethodInvokeExpr x) {
        format(x.getArguments());

        String methodName = x.getMethodName().toLowerCase();
        if ("find_in_set".equals(methodName)) {
            // 自定义处理 find_in_set(x, 'xxxx')
            return new FindInSetMethodInvoke().invoke(x, this);
        }
        else if ("isnull".equals(methodName)) {
            return new IsNullSetMethodInvoke().invoke(x, this);
        }
        else {
            return super.visit(x);
        }
    }

    public void toPrintExpr(SQLExpr x) {
        super.printExpr(x, parameterized);
    }

    public void toPrintExpr(SQLExpr x, boolean parameterized) {
        super.printExpr(x, parameterized);
    }

    public void preVisitCheck(KingbaseOutputVisitor visitor, SQLObject x) {

        if (x instanceof SQLIdentifierExpr) {
            format((SQLIdentifierExpr) x);
        } else if (x instanceof SQLPropertyExpr) {
            format((SQLPropertyExpr) x);
        } else if (x instanceof SQLInsertStatement) {
            format((SQLInsertStatement) x);
        } else if (x instanceof SQLCreateTableStatement) {
            SQLExpr expr = ((SQLCreateTableStatement) x).getName();
            format(expr);
        } else if (x instanceof SQLExprTableSource) {
            SQLExpr expr = ((SQLExprTableSource) x).getExpr();
            format(expr);
        }
    }

    private void format(SQLIdentifierExpr x) {
        x.setName(normalizeIdent(x.getName()));
    }

    private void format(SQLPropertyExpr x) {
        x.setName(normalizeIdent(x.getName()));
    }

    private void format(SQLInsertStatement x) {
        List<SQLExpr> columns = x.getColumns();
        if (columns == null || columns.size() == 0) {
            return;
        }
        for (SQLExpr column : columns) {
            format((SQLIdentifierExpr) column);
        }
    }

    private void format(SQLExpr expr) {
        if (expr instanceof SQLIdentifierExpr) {
            format((SQLIdentifierExpr) expr);
        } else if (expr instanceof SQLPropertyExpr) {
            format((SQLPropertyExpr) expr);
        }
    }

    private void format(List<SQLExpr> expr) {
        if(null == expr || expr.size() == 0) {
            return;
        }

        for (SQLExpr sqlExpr : expr) {
            format(sqlExpr);
        }
    }

    private String normalizeIdent(String ident) {
        if(null == ident)  {
            return null;
        }
        if("*".equals(ident)) {
            return ident;
        }
        ident = ident.replaceAll("'", "");
        ident = ident.replaceAll("`", "");
        ident = ident.replaceAll("\"", "");
        return "\"" + ident + "\"";
    }
}
